package com.cloud.easy.im;

import com.cloud.easy.im.container.CaffeineCache;
import com.cloud.easy.im.container.OnlineMap;
import com.cloud.easy.im.event.MessageQoSEventListenerS2C;
import com.cloud.easy.im.event.ServerEventListener;
import com.cloud.easy.im.handler.AbstractServerHandler;
import com.cloud.easy.im.handler.ServerWebSocketCoreHandler;
import com.cloud.easy.im.handler.disruptor.EventProducer;
import com.cloud.easy.im.netty.handler.NioWebSocketHandler;
import com.cloud.easy.im.netty.handler.UDPClientInboundHandler;
import com.cloud.easy.im.processor.OnlineProcessor;
import com.cloud.easy.im.utils.RedisProvider;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;

import java.util.Objects;

/**
 * 服务端应用入口主类
 * <p>
 * 如果要重置在线容器，需要重写initOnlineMap()否则使用默认容器存放于内存中
 *
 * @author wangjian
 * @version 0.1.0
 * @create 2020年09月02日5:32 下午
 */
public class BaseServerApplication {

    private static final Logger logger = LoggerFactory.getLogger(BaseServerApplication.class);

    /**
     * UDP Session的空闲超时时长（单位：秒），默认10秒
     **/
    private static final int SESION_RECYCLER_EXPIRE = 10;

    /**
     * 是否运行中
     **/
    private volatile boolean running = false;

    /**
     * 构建启动类
     */
    private final Builder builder;

    public Builder getBuilder() {
        return builder;
    }

    private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    private final EventLoopGroup workerGroup = new NioEventLoopGroup(1);
    private Channel nettyChannel = null;

    /*s*
     * 通过{@link Builder}构建服务端入口程序
     *
     * @param builder
     */
    private BaseServerApplication(Builder builder) {
        this.builder = builder;
    }

    /**
     * 返回QoS机制的Server主动消息发送之质量保证事件监听器对象。
     *
     * @return
     */
    public MessageQoSEventListenerS2C getMessageQoSEventListenerS2C() {
        return builder.getMessageQoSEventListenerS2C();
    }

    public boolean isRunning() {
        return running;
    }

    public synchronized void startUp() throws InterruptedException {
        if (isRunning()) {
            logger.warn("[start-run] 服务已启动，请勿重复启动");
            return;
        }

        // 服务端 Netty 线程池
        ServerBootstrap bootstrap = initServerBootstrap();
        ChannelFuture cf = bootstrap.bind(builder.port).syncUninterruptibly();
        logger.info("[start-run] 服务正在端口" + builder.port + "上监听中...");
        nettyChannel = cf.channel();
        initOnlineMap(); // 初始化在线用户 Channel 容器

        // 启动 Redis
        if (builder.runRedis) {
            Jedis jedis = RedisProvider.getResource();
            if (Objects.isNull(jedis)) {
                logger.warn("[start-run] 启动jedis失败");
            }
        }

        this.running = true;

        nettyChannel.closeFuture().await();
    }

    public synchronized void shutdown() {
        if (nettyChannel != null) {
            nettyChannel.close();
        }

        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();

        this.running = false;
    }

    /**
     * 初始化在线用户容器
     */
    private void initOnlineMap() {
        //OnlineProcessor.setOnlineChannels(new DefaultOnlineMap());
        //使用Caffeine-cache作为高性能在线队列
        OnlineProcessor.setOnlineChannels(builder.onlineChannels);
    }

    /**
     * 初始化 Netty的服务辅助启动类。
     *
     * @return
     */
    private ServerBootstrap initServerBootstrap() {
        return new ServerBootstrap()
                .group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                // BACKLOG用于构造服务端套接字ServerSocket对象，标识当服务器请求处理线程全满时，
                // 用于临时存放已完成三次握手的请求的队列的最大长度
                .option(ChannelOption.SO_BACKLOG, 1024)
                // 设置保持活动连接状态
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                //关闭Nagle算法
                .childOption(ChannelOption.TCP_NODELAY, true)
                // 设置用于请求提供服务的处理器
                .childHandler(initChildChannelHandler4Netty());
    }

    /**
     * 初始化针对Netty客户端的handler链
     * 默认提供：
     * 1.	UDP Session空闲超时处理器（netty内建）
     * 2.	UDP客户端处理器（框架自建）
     *
     * @return
     */
    private ChannelHandler initChildChannelHandlerForUDP() {
        return new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel ch) throws Exception {
                //设置log监听器，并且日志级别为debug，方便观察运行流程
                ch.pipeline()
                        .addLast(new ReadTimeoutHandler(SESION_RECYCLER_EXPIRE))
                        //websocket客户端处理器（框架自建）
                        .addLast(new UDPClientInboundHandler(builder.abstractServerHandler));
            }
        };
    }

    /**
     * 初始化针对Netty webSocket客户端的handler链
     *
     * @return
     */
    private ChannelHandler initChildChannelHandler4Netty() {
        return new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel ch) throws Exception {
                ch.pipeline()
                        //设置log监听器，并且日志级别为debug，方便观察运行流程
                        .addLast("logging", new LoggingHandler(builder.logLevel))
                        //设置解码器
                        .addLast("http-codec", new HttpServerCodec())//设置解码器
                        //聚合器，使用websocket会用到
                        .addLast("aggregator", new HttpObjectAggregator(65536))
                        //用于大数据的分区传输
                        .addLast("http-chunked", new ChunkedWriteHandler())//用于大数据的分区传输
                        //websocket客户端处理器（框架自建）
                        .addLast("handler", new NioWebSocketHandler(builder.abstractServerHandler));
            }
        };
    }

    /**
     * im启动配置内部类
     */
    public static class Builder {

        /**
         * 服务端端口号
         * 默认：7788
         **/
        private int port = 7788;

        /**
         * 是否启动告诉缓存方案redis
         */
        private boolean runRedis = false;

        /**
         * 日志级别
         * 默认使用：{@link LogLevel#INFO}
         */
        private LogLevel logLevel = LogLevel.INFO;

        /**
         * 服务监听处理器
         */
        private ServerEventListener serverEventListener = null;

        /**
         * qos消息监听器
         */
        private MessageQoSEventListenerS2C messageQoSEventListenerS2C = null;

        /**
         * MobileIMSDK框架的核心通信逻辑实现类（实现的是MobileIMSDK服务端的通信处理核心算法）
         * 默认使用：{@link ServerWebSocketCoreHandler}
         **/
        protected AbstractServerHandler abstractServerHandler = new ServerWebSocketCoreHandler();

        /**
         * 在线队列
         * 默认使用：{@link CaffeineCache}
         */
        private OnlineMap onlineChannels = new CaffeineCache();

        public BaseServerApplication build() {
            this.abstractServerHandler.setServerEventListener(serverEventListener);
            // TODO bufferSize 2048
            this.abstractServerHandler.setEventProducer(new EventProducer(2048, serverEventListener,
                    messageQoSEventListenerS2C, abstractServerHandler));
            this.onlineChannels.setServerEventListener(serverEventListener);
            return new BaseServerApplication(this);
        }

        public Builder setPort(int port) {
            this.port = port;
            return this;
        }

        public Builder setServerEventListener(ServerEventListener serverEventListener) {
            this.serverEventListener = serverEventListener;
            return this;
        }

        public ServerEventListener getServerEventListener() {
            return serverEventListener;
        }

        public Builder setMessageQoSEventListenerS2C(MessageQoSEventListenerS2C messageQoSEventListenerS2C) {
            this.messageQoSEventListenerS2C = messageQoSEventListenerS2C;
            return this;
        }

        public MessageQoSEventListenerS2C getMessageQoSEventListenerS2C() {
            return messageQoSEventListenerS2C;
        }

        public Builder setLogLevel(LogLevel logLevel) {
            this.logLevel = logLevel;
            return this;
        }

        public Builder setAbstractServerHandler(AbstractServerHandler abstractServerHandler) {
            this.abstractServerHandler = abstractServerHandler;
            return this;
        }

        public Builder setOnlineChannels(OnlineMap onlineChannels) {
            this.onlineChannels = onlineChannels;
            return this;
        }

        public Builder setRunRedis(boolean runRedis) {
            this.runRedis = runRedis;
            return this;
        }
    }
}
